#include "stdafx.h"
#include "ItemIDList.h"
#include <gtl/string/trans.h>

CItemIDList::CItemIDList(CShellMalloc* pShellMalloc)
{
	m_bWrap = false;
	m_pItemIDList = NULL;
	m_pShellMalloc = pShellMalloc;
}

CItemIDList::CItemIDList(const CString& strPathName, CShellMalloc* pShellMalloc)
{
	m_bWrap = false;
	*this = strPathName;
	m_pShellMalloc = pShellMalloc;
}

CItemIDList::CItemIDList(LPITEMIDLIST pidlFullyQual, bool bWrap, CShellMalloc* pShellMalloc)
{
	m_pShellMalloc = pShellMalloc;

	if(bWrap)
	{
		m_bWrap = true;
		m_pItemIDList = pidlFullyQual;
	}
	else if(pidlFullyQual != NULL)
	{
		m_bWrap = false;
		*this = pidlFullyQual;
	}
}

CItemIDList::CItemIDList(const CItemIDList& other)
{
	*this = other;
}

CItemIDList::~CItemIDList(void)
{
	FreeIDList();
}

void CItemIDList::FreeIDList()
{
	if(m_pItemIDList != NULL && !m_bWrap)
	{
		if(m_pShellMalloc == NULL)
			CShellMalloc().Free(m_pItemIDList);
		else
			m_pShellMalloc->Free(m_pItemIDList);
	}
	m_pItemIDList = NULL;
}

const CItemIDList& CItemIDList::operator=(const CString& strPathName)
{
	FreeIDList();
	
	IShellFolder* psfDeskTop  = NULL;
	if(SUCCEEDED(::SHGetDesktopFolder(&psfDeskTop)))
	{
		// Get the folders IDL
		psfDeskTop->ParseDisplayName(NULL, NULL, (LPOLESTR)XTP_CT2CW(strPathName), NULL, &m_pItemIDList, NULL);
		psfDeskTop->Release();
	}

	return *this;
}

const CItemIDList& CItemIDList::operator=(LPITEMIDLIST lpFullyIDList)
{
	FreeIDList();

	if(lpFullyIDList != NULL)
	{
		UINT nTotalBytes = GetTotalSize(lpFullyIDList);
		m_pItemIDList = CreateIDList(CShellMalloc(), nTotalBytes);
		if(m_pItemIDList != NULL)
			memcpy(m_pItemIDList, lpFullyIDList, nTotalBytes);
	}

	return *this;
}

const CItemIDList& CItemIDList::operator=(const CItemIDList& other)
{
	*this = other.m_pItemIDList;
	m_bWrap = other.m_bWrap;
	m_pShellMalloc = other.m_pShellMalloc;
	return *this;
}

CItemIDList::operator LPITEMIDLIST() const
{
	return m_pItemIDList;
}

LPITEMIDLIST CItemIDList::operator->()
{
	return m_pItemIDList;
}

bool CItemIDList::operator==(const CItemIDList& other)
{
	return operator==(other.m_pItemIDList);
}

bool CItemIDList::operator==(LPITEMIDLIST pidlFullyQual)
{
	if(m_pItemIDList == NULL || pidlFullyQual == NULL)
		return false;

	LPSHELLFOLDER psfDesktop = NULL;
	if(FAILED(::SHGetDesktopFolder(&psfDesktop)))
		return false;

	HRESULT hr = psfDesktop->CompareIDs(0, m_pItemIDList, pidlFullyQual);
	psfDesktop->Release();

	return ((short)hr) == 0;
}

const CItemIDList& CItemIDList::Attach(LPITEMIDLIST lpIDList)
{
	FreeIDList();
	m_pItemIDList = lpIDList;
	return *this;
}

LPITEMIDLIST CItemIDList::Detach()
{
	LPITEMIDLIST lpIDList = m_pItemIDList;
	m_pItemIDList = NULL;
	return lpIDList;
}

void CItemIDList::Move(CItemIDList& other)
{
	Attach(other.m_pItemIDList);
	other.Detach();
}

LPITEMIDLIST CItemIDList::CreateIDList(LPMALLOC lpMalloc, UINT cbSize)
{
	if(lpMalloc == NULL)
		return NULL;

	// zero-init for external task allocate
	LPITEMIDLIST pIDList = (LPITEMIDLIST)lpMalloc->Alloc(cbSize);
	if(pIDList != NULL)
		memset(pIDList, 0, cbSize);

	return pIDList;
}

LPITEMIDLIST CItemIDList::GetLastItem(LPITEMIDLIST lpIDList)
{
	if(lpIDList == NULL)
		return NULL;
		
	LPSTR lpMem = (LPSTR)lpIDList;
	LPITEMIDLIST pItemLast = NULL;
	// walk the list until we find a null
	while(*lpMem != 0)
	{
		if(LPITEMIDLIST(lpMem)->mkid.cb == 0) 
			break; // maybe ?

		pItemLast = (LPITEMIDLIST)lpMem;
		lpMem += LPITEMIDLIST(lpMem)->mkid.cb;
	}

	return pItemLast;
}

LPITEMIDLIST CItemIDList::GetNextItem(LPITEMIDLIST lpIDList)
{
	if(lpIDList == NULL)
		return NULL;
		
	LPSTR lpMem = (LPSTR)lpIDList;
	lpMem += lpIDList->mkid.cb;
	return (LPITEMIDLIST)lpMem;
}

UINT CItemIDList::GetItemCount(LPITEMIDLIST lpIDList /* = NULL */)
{
	lpIDList = (lpIDList == NULL ? m_pItemIDList : lpIDList);
	if(lpIDList == NULL)
		return 0;

	UINT nCount = 0;
	while(lpIDList->mkid.cb != 0)
	{
		++nCount;
		lpIDList = GetNextItem(lpIDList);
	}

	return nCount;
}

UINT CItemIDList::GetTotalSize(LPITEMIDLIST lpIDList /* = NULL */)
{
	lpIDList = (lpIDList == NULL ? m_pItemIDList : lpIDList);
	if(lpIDList == NULL)
		return 0;

	UINT nTotalSize = sizeof(lpIDList->mkid.cb); // Null terminator
	while(lpIDList->mkid.cb)
	{
		nTotalSize += lpIDList->mkid.cb;
		lpIDList = GetNextItem(lpIDList);
	}

	return nTotalSize;
}

CString CItemIDList::GetName()
{
	CString strName;
	return GetName(strName);
}

CString CItemIDList::GetPath()
{
	CString strPath;
	return GetName(strPath);
}

const CString& CItemIDList::GetName(CString& strName)
{
	if(m_pItemIDList == NULL)
		return strName;
		
	SHFILEINFO sfi = {0};
	SHGetFileInfo((LPCTSTR)m_pItemIDList, 0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_DISPLAYNAME);
	if(_tcslen(sfi.szDisplayName) != 0)
		strName = sfi.szDisplayName;

	return strName;
}

const CString& CItemIDList::GetPath(CString& strPath)
{
	if(m_pItemIDList == NULL)
		return strPath;
		
	::SHGetPathFromIDList(m_pItemIDList, strPath.GetBuffer(512));
	strPath.ReleaseBuffer();

	return strPath;
}

BOOL CItemIDList::GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST lpIDList, DWORD dwFlags, CString& strFriendlyName)
{
	STRRET str;
	if (lpsf->GetDisplayNameOf(lpIDList, dwFlags, &str) == NOERROR)
	{
		switch (str.uType)
		{
		case STRRET_WSTR:
			{
				strFriendlyName = str.pOleStr;

				if(m_pShellMalloc == NULL)
					CShellMalloc().Free(str.pOleStr);
				else
					m_pShellMalloc->Free(str.pOleStr);
			}
			break;

		case STRRET_OFFSET:
			{
				LPSTR lpszOffset = (LPSTR)lpIDList + str.uOffset;
				strFriendlyName = lpszOffset;
			}
			break;

		case STRRET_CSTR:
			strFriendlyName = (LPSTR)str.cStr;
			break;

		default:
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	return TRUE;
}

bool CItemIDList::IsDesktop(LPITEMIDLIST pidlFullQual /* = NULL */)
{
	pidlFullQual= (pidlFullQual == NULL ? m_pItemIDList : pidlFullQual);
	if(pidlFullQual == NULL)
		return true;

	return pidlFullQual->mkid.cb == 0;
}

LPITEMIDLIST CItemIDList::Up(LPITEMIDLIST pidlFullQual /* = NULL */)
{
	pidlFullQual= (pidlFullQual == NULL ? m_pItemIDList : pidlFullQual);
	if(pidlFullQual == NULL)
		return NULL;
		
	LPITEMIDLIST pItemLast = GetLastItem(pidlFullQual);
	if(pItemLast != NULL)
	{
		// don't attempt to go any lower than 1 IDL
		if (pItemLast == pidlFullQual) 
			return pidlFullQual;
		// set its cb to 0
		pItemLast->mkid.cb = 0;
	}

	return pidlFullQual;
}

